from rest_framework import serializers
from . import models
from rest_framework import exceptions
from django.conf import settings

class LoginSerializer(serializers.ModelSerializer):
    # 覆盖，避免login校验username有数据库唯一字段约束的限制
    username = serializers.CharField()
    class Meta:
        model = models.User
        # username、password 可以通过局部钩子指定详细的校验规则
        fields = ('id','username','password','icon')
        extra_kwargs = {
            'id': {
                'read_only': True,
            },
            'icon': {
                'read_only': True,
            },
            'password': {
                'write_only': True,
            }
        }

    def validate(self, attrs):
        # 多方式得到user
        user = self._get_user(attrs)
        # user 签发token
        token = self._get_token(user)
        # token 用context 属性携带给视图类
        self.context['token'] = token
        '''  自己将user的信息逐个处理传给视图
        # 前台可能不仅仅需要token,可能还需要用户名，头像等
        self.context['username'] = user.username
        # 通过请求头格式化icon
        request = self.context['request']
        icon = 'http://%s%s%s' % (request.META['HTTP_HOST'],settings.MEDIA_URL,user.icon)
        self.context['icon'] = icon
        '''
        # 将登录user对象直接传给视图
        self.context['user'] = user
        return attrs

    def _get_user(self,attrs):
        import re
        username = attrs.get('username')
        if re.match(r'1[3-9][0-9]{9}$',username):
            user = models.User.objects.filter(mobile=username).first()
        # elif re.match(r'^.*@.*$',username):   # email 不是唯一约束，该功能不能提供
        #     user = models.User.objects.filter(email=username).first()
        else:
            user = models.User.objects.filter(username=username).first()
        if not user:
            raise exceptions.ValidationError({'username':'username error'})
        password = attrs.get('password')
        if not user.check_password(password):
            raise exceptions.ValidationError({'password':'password error'})

        return user

    def _get_token(self,user):
        from rest_framework_jwt.serializers import jwt_payload_handler,jwt_encode_handler
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        return token


import re
from django.core.cache import cache
class MobileLoginSerializer(serializers.ModelSerializer):
    mobile = serializers.CharField(required=True,write_only=True) # 覆盖
    code = serializers.CharField(min_length=4,max_length=4,required=True,write_only=True) # 自定义
    class Meta:
        model = models.User
        fields = ('id', 'username', 'icon','mobile','code')
        extra_kwargs = {
            'id': {
                'read_only': True,
            },
            'icon': {
                'read_only': True,
            },
            'username': {
                'read_only': True,
            }
        }

    # 手机号格式校验（手机号是否存在校验规则自己考量）
    def validated_mobile(self,value):
        if not re.match(r'1[3-9][0-9]{9}$',value):
            raise exceptions.ValidationError('mobile field error')
        return value

    def validate(self, attrs):
        # 验证码校验 - 需要验证码与手机号两者参与
        mobile = self._check_code(attrs)
        # 多方式得到user
        user = self._get_user(mobile)
        # user 签发token
        token = self._get_token(user)
        # token 用context 属性携带给视图类
        self.context['token'] = token
        # 将登录user对象直接传给视图
        self.context['user'] = user
        return attrs

    def _check_code(self,attrs):
        # 验证码校验，需要验证码与手机号两者参与
        mobile = attrs.get('mobile')
        code = attrs.pop('code')
        old_code = cache.get(settings.SMS_CACHE_KEY % {'mobile':mobile})
        if code != old_code:
            raise exceptions.ValidationError({'code':'double code error'})
        else:
            # 验证码的时效性：一旦验证码验证通过，代表该验证码已使用，需要立即失效
            # cache.set(settings.SMS_CACHE_KEY % {'mobile':mobile},'',-1)
            pass
        return mobile
    def _get_user(self, mobile):
        try:
            return models.User.objects.get(mobile=mobile)
        except:
            raise exceptions.ValidationError({'mobile':'user not exist'})

    def _get_token(self, user):
        from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        return token

class RegisterSerializer(serializers.ModelSerializer):
    code = serializers.CharField(min_length=4,max_length=4,required=True,write_only=True) # 自定义
    class Meta:
        model = models.User
        fields = ('mobile','password','code')
        extra_kwargs = {
            'password': {
                'min_length':8,
                'max_length':10,
                'write_only':True
            }
        }
    # 手机号格式校验（手机号是否存在校验规则自己考量）
    def validated_mobile(self,value):
        if not re.match(r'1[3-9][0-9]{9}$',value):
            raise exceptions.ValidationError('mobile field error')
        return value

    def validate(self, attrs):
        # 验证码校验，需要验证码与手机号两者参与
        mobile = attrs.get('mobile')
        code = attrs.pop('code')
        old_code = cache.get(settings.SMS_CACHE_KEY % {'mobile':mobile})
        if code != old_code:
            raise exceptions.ValidationError({'code':'double code error'})
        else:
            # 验证码的时效性：一旦验证码验证通过，代表该验证码已使用，需要立即失效
            # cache.set(settings.SMS_CACHE_KEY % {'mobile':mobile},'',-1)
            pass

        # 数据入库必须需要唯一账号：
        # 1） 前台注册必须提供账号
        # 2） 默认用手机号作为账号名，后期可以修改
        attrs['username'] = mobile
        return attrs

    # 我们在写注册接口的序列化类时，重写了create方法
    # i）注册接口操作的是User表，且入库信息中有password字段，该字段需要密文处理
    # ii）注册接口完成的是增数据入库操作，使用在视图类中serializer.save()
    # 本质是调用序列化类的create方法完成入库的
    # iii）默认入库调用的是model类的create方法，这样操作密码是明文，所以重写序列化类的create方法，调用auth组件封装的create_user()
    # 方法完成入库(密码会密文)
    def create(self, validated_data):  # 入库的数据:mobile,password,username
        return models.User.objects.create_user(**validated_data)


